package com.yupi.springbootinit.aop;

import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.yupi.springbootinit.annotation.Retryer;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class RetryerAspect {


    @Around("@annotation(com.yupi.springbootinit.annotation.Retryer)")
    public Object monitorAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method;
        if(joinPoint.getSignature() instanceof MethodSignature){
            MethodSignature signature  = (MethodSignature) joinPoint.getSignature();
            method = signature.getMethod();
        }else {
            log.info("切点不是一个方法{}",joinPoint);
            return null;
        }

        Retryer retryerAnnotation = method.getAnnotation(Retryer.class);
        if(retryerAnnotation.maxAttempt()<=0||retryerAnnotation.maxDelayMsec()<=0){
            //不重试直接执行并返回
            return joinPoint.proceed();
        }

        RetryerBuilder retryer = RetryerBuilder.newBuilder();
        //配置retryer的属性
        if(retryerAnnotation.waitMsec()>0){//如果有重试等待时间>0
            retryer.withWaitStrategy(WaitStrategies.fixedWait(retryerAnnotation.waitMsec(), TimeUnit.SECONDS));
        }

        if(retryerAnnotation.retryThrowable().length>0){//如果有重试数据源
            for (Class retryThrowable : retryerAnnotation.retryThrowable()) {
                if(retryThrowable != null&& Throwable.class.isAssignableFrom(retryThrowable)){
                    retryer.retryIfExceptionOfType(retryThrowable);
                }
            }
        }

        //停止策略只能二选一
        if(retryerAnnotation.maxDelayMsec()>0){//最大超时时间
            retryer.withStopStrategy(StopStrategies.stopAfterDelay(retryerAnnotation.maxDelayMsec(),TimeUnit.SECONDS));
        } else if(retryerAnnotation.maxAttempt()>0){
            retryer.withStopStrategy(StopStrategies.stopAfterAttempt(retryerAnnotation.maxAttempt()));
        }

        String retrylog = joinPoint.getTarget().getClass().getCanonicalName()+"."+method.getName();
        return retryer.build().call(new Callable() {
            int i = 0;
            @Override
            public Object call() throws Exception {
                try {
                    i++;
                    log.info("第{}次执行！", i);
                    log.info("<RETRYER>" + retrylog);
                    return joinPoint.proceed();
                } catch (Throwable throwable) {
                    if (throwable instanceof Exception) {
                        throw (Exception) throwable;
                    } else {
                        throw new Exception(throwable);
                    }
                }
            }
        });


    }
}
